package com.liaoyifan.core.util;

import com.liaoyifan.core.constant.Constants;
import com.liaoyifan.core.enums.PoolNameEnum;
import com.liaoyifan.core.thread.ThreadPool;
import de.mkammerer.argon2.Argon2;
import de.mkammerer.argon2.Argon2Constants;
import de.mkammerer.argon2.Argon2Factory;
import de.mkammerer.argon2.Argon2Factory.Argon2Types;
import java.util.concurrent.*;
import java.util.function.Consumer;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import lombok.extern.log4j.Log4j2;

@Log4j2
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class Password {

    // 默认安全参数配置
    private static final int PARALLELISM = Math.max(1, Constants.CPU_COUNT / 2); // 并行度 (线程数)
    private static final int ITERATIONS = 4; // 迭代次数
    private static final int MEMORY = 65536; // 内存使用量64M
    private static final int SALT_LENGTH = Argon2Constants.DEFAULT_SALT_LENGTH; // 盐值长度 (bytes)
    private static final int HASH_LENGTH = Argon2Constants.DEFAULT_HASH_LENGTH; // 哈希值长度 (bytes)
    private static final long PERFORMANCE_WARN_THRESHOLD = 1500; // 毫秒
    // 单例 Argon2 实例
    private static final Argon2 ARGON2 =
            Argon2Factory.create(Argon2Types.ARGON2id, SALT_LENGTH, HASH_LENGTH);

    /**
     * 对明文密码进行哈希处理
     *
     * @param plainPassword 明文密码
     * @return 哈希后的密码字符串
     */
    public static String hash(String plainPassword) {
        if (plainPassword == null || plainPassword.isEmpty()) {
            throw new IllegalArgumentException("密码不能为空");
        }
        char[] passwordChars = plainPassword.toCharArray();

        try {
            long startTime = System.nanoTime();
            String hash = ARGON2.hash(ITERATIONS, MEMORY, PARALLELISM, passwordChars);
            long duration = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startTime);

            if (duration > PERFORMANCE_WARN_THRESHOLD) {
                log.warn("密码哈希耗时过长: {} ms", duration);
            }
            return hash;
        } catch (IllegalArgumentException | IllegalStateException e) {
            throw new SecurityException("Argon2 密码哈希失败", e);
        } finally {
            ARGON2.wipeArray(passwordChars);
        }
    }

    /**
     * 验证密码是否匹配
     *
     * @param plainPassword 明文密码
     * @param hashedPassword 哈希后的密码
     * @return 如果密码匹配返回 true，否则返回 false
     */
    public static boolean verify(String plainPassword, String hashedPassword) {
        return verify(plainPassword, hashedPassword, null);
    }

    /**
     * 验证密码是否匹配
     *
     * @param plainPassword 明文密码
     * @param hashedPassword 哈希后的密码
     * @param passwordUpdateCallBack 更新hash回调
     * @return 如果密码匹配返回 true，否则返回 false
     */
    public static boolean verify(
            String plainPassword, String hashedPassword, Consumer<String> passwordUpdateCallBack) {
        if (plainPassword == null
                || plainPassword.isEmpty()
                || hashedPassword == null
                || hashedPassword.isEmpty()) {
            return false;
        }
        char[] passwordChars = plainPassword.toCharArray();
        try {
            long startTime = System.nanoTime();
            boolean isMatch = ARGON2.verify(hashedPassword, passwordChars);
            long duration = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startTime);
            if (duration > PERFORMANCE_WARN_THRESHOLD) {
                log.warn("密码验证耗时过长: {} ms", duration);
            }
            if (isMatch && ARGON2.needsRehash(hashedPassword, ITERATIONS, MEMORY, PARALLELISM)) {
                log.info("检测到需要升级密码哈希，旧参数: {}", extractParameters(hashedPassword));
                if (passwordUpdateCallBack != null) {
                    upgradePasswordHashAsync(plainPassword, hashedPassword, passwordUpdateCallBack);
                }
            }
            return isMatch;
        } catch (IllegalArgumentException | IllegalStateException e) {
            log.error("密码验证过程中发生异常", e);
            return false;
        } finally {
            ARGON2.wipeArray(passwordChars);
        }
    }

    /** 异步升级密码哈希 */
    private static void upgradePasswordHashAsync(
            String plainPassword, String oldHash, Consumer<String> passwordUpdateCallBack) {
        ThreadPool.execute(
                PoolNameEnum.PASSWORD.getValue(),
                t ->
                        t.execute(
                                () -> {
                                    try {
                                        String newHash = hash(plainPassword);
                                        if (!newHash.equals(oldHash)) {
                                            passwordUpdateCallBack.accept(newHash);
                                            log.info(
                                                    "密码哈希升级成功，新参数: {}", extractParameters(newHash));
                                        }
                                    } catch (Exception e) {
                                        log.error("密码哈希升级失败", e);
                                    }
                                }));
    }

    /** 提取哈希中的参数信息（用于日志记录） */
    private static String extractParameters(String hashedPassword) {
        Pattern pattern = Pattern.compile("\\$argon2id\\$v=\\d+\\$m=(\\d+),t=(\\d+),p=(\\d+)\\$");
        Matcher matcher = pattern.matcher(hashedPassword);
        if (matcher.find()) {
            return String.format(
                    "m=%s,t=%s,p=%s", matcher.group(1), matcher.group(2), matcher.group(3));
        }
        return "unknown";
    }

    /** 示例用法 */
    public static void main(String[] args) {

        String password = "SecurePassw0rd!";

        // 哈希密码
        String hash = hash(password);
        log.info("哈希后的密码: {}", hash);

        // 验证正确密码
        boolean isCorrect = verify(password, hash);
        log.info("正确密码验证结果: {}", isCorrect);

        // 验证错误密码
        boolean isWrong = verify("WrongPassword", hash);
        log.info("错误密码验证结果: {}", isWrong);
    }
}
